﻿using S7Demo.Base;
using S7Demo.Model;
using System;
using System.Windows;
using S7.Net;
using System.Windows.Controls.Primitives;
using System.Windows.Controls;
using static System.Net.Mime.MediaTypeNames;
using System.Net.Http.Headers;
using System.Text;
using System.Collections.Generic;

namespace S7Demo.ViewModel
{
    public class MainViewModel : NotifyBase
    {

        public ConfigModel ConfigModel { get; set; } = new ConfigModel();
        public List<string> PlcTypeList { get; set; } = new List<string>();
        public List<string> DataTypeList { get; set; } = new List<string>();

        private string _plcStatus = "连接";

        public string PlcStatus
        {
            get { return _plcStatus; }
            set { _plcStatus = value; this.NotifyChanged(); }
        }

        private string _log = "日志\r\n";

        public string log
        {
            get { return _log; }
            set { _log = value; this.NotifyChanged(); }
        }

        public MainViewModel()
        {
            ConfigModel.Ip = "192.168.0.1";
            ConfigModel.PlcType = "S71500";
            ConfigModel.Rack = 0;
            ConfigModel.Slot = 1;
            ConfigModel.WriteAddr = "DB10.";
            ConfigModel.WriteDataType = "Int";
            ConfigModel.ReadAddr = "DB10.";
            ConfigModel.ReadData = "";

            _plcStatus = "连接123";

            string[] plcTypeArr = Enum.GetNames(typeof(CpuType));
            foreach (var item in plcTypeArr)
                PlcTypeList.Add(item);

            string[] dataTypeArr = Enum.GetNames(typeof(VarType));
            foreach (var item in dataTypeArr)
                DataTypeList.Add(item);

        }

        private CommandBase _plcComm;

        public CommandBase PlcCommand
        {
            get
            {
                if (_plcComm == null)
                {
                    _plcComm = new CommandBase();
                    _plcComm.DoExecute = new Action<object>((obj) =>
                    {
                        connPlc();
                    });
                }
                return _plcComm;
            }
            set { _plcComm = value; }
        }

        private CommandBase _readConn;

        public CommandBase ReadCommand
        {
            get
            {
                if (_readConn == null)
                {
                    _readConn = new CommandBase();
                    _readConn.DoExecute = new Action<object>(obj =>
                     {
                         GetParam();
                     });
                }
                return _readConn;
            }
            set { _readConn = value; }
        }

        private CommandBase _writeComm;

        public CommandBase WriteCommand
        {
            get
            {
                if (_writeComm == null)
                {
                    _writeComm = new CommandBase();
                    _writeComm.DoExecute = new Action<object>((obj) =>
                    {
                        ReadData();
                    });
                }
                return _writeComm;
            }
            set { _writeComm = value; }
        }

        Plc plc;

        private void connPlc()
        {
            try
            {
                if (plc == null)
                {
                    plc = new Plc((CpuType)Enum.Parse(typeof(CpuType), ConfigModel.PlcType),
                                   ConfigModel.Ip,
                                   Convert.ToInt16(ConfigModel.Rack),
                Convert.ToInt16(ConfigModel.Slot));

                }
                if (!plc.IsConnected)
                {
                    plc.Open();
                    PlcStatus = "断开";
                    log += "打开成功！\r\n";
                }
                else
                {
                    plc.Close();
                    PlcStatus = "链接";
                    log += "连接关闭！\r\n";
                }
            }
            catch
            {
                log += "连接或断开失败，请检查设备信息是否正确！\r\n";
                return;
            }
        }

        private void GetParam()
        {
            plcInfo plcInfo1 = getPlcInfo();
            if (String.IsNullOrEmpty(plcInfo1.DataPath))
            {
                return;
            }
            string showStr = GetValue(plcInfo1).ToString();
            log += ConfigModel.WriteAddr + "内容：" + showStr + "\r\n";
        }

        private void ReadData()
        {
            plcInfo plcInfo1 = getPlcInfo();
            if (String.IsNullOrEmpty(plcInfo1.DataPath))
            {
                return;
            }
            SetValue(plcInfo1, ConfigModel.ReadData);
        }

        private plcInfo getPlcInfo()
        {
            if (plc == null || !plc.IsConnected)
            {
                log += "请先连接PLC设备！\r\n";
                return new plcInfo() { DataPath = "" };
            }
            if (String.IsNullOrEmpty(ConfigModel.WriteDataType))
            {
                log += "请先输入参数类型！\r\n";
                return new plcInfo() { DataPath = "" };
            }

            if (ConfigModel.WriteDataType == "Bit")
                return new plcInfo() { DataPath = ConfigModel.ReadAddr, DataType = VarType.Bit };
            else if (ConfigModel.WriteDataType == "Byte")
                return new plcInfo() { DataPath = ConfigModel.ReadAddr, DataType = VarType.Byte };
            else if (ConfigModel.WriteDataType == "Word")
                return new plcInfo() { DataPath = ConfigModel.ReadAddr, DataType = VarType.Word };
            else if (ConfigModel.WriteDataType == "DWord")
                return new plcInfo() { DataPath = ConfigModel.ReadAddr, DataType = VarType.DWord };
            else if (ConfigModel.WriteDataType == "Int")
                return new plcInfo() { DataPath = ConfigModel.ReadAddr, DataType = VarType.Int };
            else if (ConfigModel.WriteDataType == "DInt")
                return new plcInfo() { DataPath = ConfigModel.ReadAddr, DataType = VarType.DInt };
            else if (ConfigModel.WriteDataType == "Real")
                return new plcInfo() { DataPath = ConfigModel.ReadAddr, DataType = VarType.Real };
            else if (ConfigModel.WriteDataType == "LReal")
                return new plcInfo() { DataPath = ConfigModel.ReadAddr, DataType = VarType.LReal };
            else if (ConfigModel.WriteDataType == "String")
                return new plcInfo() { DataPath = ConfigModel.ReadAddr, DataType = VarType.String };
            else if (ConfigModel.WriteDataType == "S7String")
                return new plcInfo() { DataPath = ConfigModel.ReadAddr, DataType = VarType.S7String };
            else if (ConfigModel.WriteDataType == "S7WString")
                return new plcInfo() { DataPath = ConfigModel.ReadAddr, DataType = VarType.S7WString };
            else if (ConfigModel.WriteDataType == "S5Time")
                return new plcInfo() { DataPath = ConfigModel.ReadAddr, DataType = VarType.S5Time };
            else
                log += "未知的数据类型 "+ ConfigModel.WriteDataType + "\r\n";
            return new plcInfo() { DataPath = "" };
        }

        #region S7 协议操作

        private object GetValue(plcInfo plcInfo)
        {
            if (plc == null || !plc.IsConnected)
            {
                return "请先连接PLC设备！";
            }

            try
            {
                var plcData = new PLCAddress(plcInfo.DataPath);
                VarType useType = plcInfo.DataType;
                switch (useType)
                {
                    case VarType.Bit:
                        return plc.Read(plcInfo.DataPath);
                        break;
                    case VarType.Byte:
                        return plc.Read(plcInfo.DataPath);
                        break;
                    case VarType.Word:
                        return plc.Read(plcInfo.DataPath);
                        break;
                    case VarType.DWord:
                        return plc.Read(plcInfo.DataPath);
                        break;
                    case VarType.Int:
                        return plc.Read(plcData.DataType, plcData.DbNumber, plcData.StartByte, VarType.Int, 1);
                        break;
                    case VarType.DInt:
                        return plc.Read(plcData.DataType, plcData.DbNumber, plcData.StartByte, VarType.DInt, 1);
                        break;
                    case VarType.Real:
                        return plc.Read(plcData.DataType, plcData.DbNumber, plcData.StartByte, VarType.Real, 1);
                        break;
                    case VarType.LReal:
                        return plc.Read(plcData.DataType, plcData.DbNumber, plcData.StartByte, VarType.LReal, 1);
                        break;
                    case VarType.String:
                        byte count = (byte)plc.Read(plcData.DataType, plcData.DbNumber, plcData.StartByte + 1, VarType.Byte, 1);
                        return plc.Read(plcData.DataType, plcData.DbNumber, plcData.StartByte + 2, VarType.String, count);
                        break;
                    case VarType.S7String:
                        byte S7StringCount = (byte)plc.Read(plcData.DataType, plcData.DbNumber, plcData.StartByte + 1, VarType.Byte, 1);
                        return plc.Read(plcData.DataType, plcData.DbNumber, plcData.StartByte, VarType.S7String, S7StringCount);
                        break;
                    case VarType.S7WString:
                        short S7WStringCount = ((short)plc.Read(plcData.DataType, plcData.DbNumber, plcData.StartByte + 2, VarType.Int, 1));
                        return plc.Read(plcData.DataType, plcData.DbNumber, plcData.StartByte, VarType.S7WString, S7WStringCount);
                        break;
                    case VarType.S5Time:
                        return plc.Read(plcData.DataType, plcData.DbNumber, plcData.StartByte, VarType.S5Time, 1);
                        break;
                    case VarType.Counter:
                        break;
                    case VarType.DateTime:
                        return plc.Read(plcData.DataType, plcData.DbNumber, plcData.StartByte, VarType.DateTime, 1);
                        break;
                    case VarType.DateTimeLong:
                        return plc.Read(plcData.DataType, plcData.DbNumber, plcData.StartByte, VarType.DateTimeLong, 1);
                        break;
                    default:
                        throw new Exception("无输入");
                        break;
                }
                return null;
            }
            catch
            {
                return "获取数据失败！";
            }
        }


        private void SetValue(plcInfo plcInfo, object value)
        {
            try
            {
                var plcData = new PLCAddress(plcInfo.DataPath);
                VarType useType = plcInfo.DataType;

                switch (useType)
                {
                    case VarType.Bit:
                        plc.Write(plcData.DataType, plcData.DbNumber, plcData.StartByte, Convert.ToBoolean(value));
                        break;
                    case VarType.Byte:
                        plc.Write(plcData.DataType, plcData.DbNumber, plcData.StartByte, Convert.ToByte(value));
                        break;
                    case VarType.Word:
                        plc.Write(plcData.DataType, plcData.DbNumber, plcData.StartByte, Convert.ToUInt16(value));
                        break;
                    case VarType.DWord:
                        plc.Write(plcData.DataType, plcData.DbNumber, plcData.StartByte, Convert.ToUInt32(value));
                        break;
                    case VarType.Int:
                        plc.Write(plcData.DataType, plcData.DbNumber, plcData.StartByte, Convert.ToInt16(value));
                        break;
                    case VarType.DInt:
                        plc.Write(plcData.DataType, plcData.DbNumber, plcData.StartByte, Convert.ToInt32(value));
                        break;
                    case VarType.Real:
                        plc.Write(plcData.DataType, plcData.DbNumber, plcData.StartByte, Convert.ToSingle(value));
                        break;
                    case VarType.LReal:
                        plc.Write(plcData.DataType, plcData.DbNumber, plcData.StartByte, Convert.ToDouble(value));
                        break;
                    case VarType.String:
                        byte Count = Convert.ToByte(plcInfo.ByteCount == 0 ? 255 : plcInfo.ByteCount);
                        byte UsingCount = Convert.ToByte(value.ToString().Length);
                        byte[] stringArr = new byte[value.ToString().Length + 2];
                        stringArr[0] = Count;
                        stringArr[1] = UsingCount;
                        byte[] OstringArr = Encoding.ASCII.GetBytes(value.ToString());
                        for (int i = 0; i < OstringArr.Length; i++)
                        {
                            stringArr[2 + i] = OstringArr[i];
                        }
                        plc.Write(plcData.DataType, plcData.DbNumber, plcData.StartByte, stringArr);
                        break;
                    case VarType.S7String:
                        plc.Write(plcData.DataType, plcData.DbNumber, plcData.StartByte, value.ToString());
                        break;
                    case VarType.S7WString:
                        short wsCount = Convert.ToInt16(plcInfo.ByteCount == 0 ? 255 : plcInfo.ByteCount);
                        short wsUsingCount = Convert.ToInt16(value.ToString().Length);
                        byte[] wsstringArr = new byte[value.ToString().Length * 2 + 4];
                        wsstringArr[0] = Convert.ToByte(wsCount >> 8);
                        wsstringArr[1] = Convert.ToByte(wsCount & 0x00FF);
                        wsstringArr[2] = Convert.ToByte(wsUsingCount >> 8);
                        wsstringArr[3] = Convert.ToByte(wsUsingCount & 0x00FF);
                        byte[] wsOstringArr = Encoding.BigEndianUnicode.GetBytes(value.ToString());
                        for (int i = 0; i < wsOstringArr.Length; i++)
                        {
                            wsstringArr[4 + i] = wsOstringArr[i];
                        }
                        plc.Write(plcData.DataType, plcData.DbNumber, plcData.StartByte, wsstringArr);
                        break;
                    case VarType.S5Time:
                        short time = Convert.ToInt16(value);
                        plc.Write(plcData.DataType, plcData.DbNumber, plcData.StartByte, time);
                        break;
                    //case VarType.Counter:
                    //    break;
                    //case VarType.DateTime:
                    //    showStr = plc.Read(plcData.DataType, plcData.DbNumber, plcData.StartByte, VarType.DateTime, 1).ToString();
                    //    break;
                    //case VarType.DateTimeLong:
                    //    showStr = plc.Read(plcData.DataType, plcData.DbNumber, plcData.StartByte, VarType.DateTimeLong, 1).ToString();
                    //    break;
                    default:
                        break;
                }
                log += "写入完成！\r\n";
            }
            catch
            {
                log += "写入设备失败！\r\n";
            }
        }

        #endregion

        struct plcInfo
        {
            public string DataPath;
            public VarType DataType;
            public int ByteCount;
        }

    }
}
